#include "stdafx.h"
#include "GlyphGen.h"

namespace dotfont{

//----------------------------------------------------------------------------//

GlyphGen* GlyphGenFactory::create( const wstring& FontName, float FontSize, 
								  DotFont::Style Style)
{
	return new BitmapGen(FontName, FontSize, Style);
}

GlyphGen* GlyphGenFactory::create( const wstring& FontName, float FontSize, 
								  DotFont::Style Style, DWORD StrokeColor )
{
	return new StrokeBmpGen(FontName, FontSize, Style, StrokeColor);
}

//----------------------------------------------------------------------------//

BitmapGen::BitmapGen( const wstring& FontName, float FontSize, DotFont::Style Style) 
	: fontName(FontName), fontSize(FontSize), style(Style), font(NULL)
{
	font = new Font(fontName.c_str(), fontSize, static_cast<FontStyle>(style), UnitPixel);
	if (font->GetLastStatus() != Ok)
	{
		delete font;
		font = NULL;
		throw runtime_error("Create font of failed!");
	}
}

void BitmapGen::gen(wstring::const_iterator begin, const wstring::const_iterator& end,  
					int nRow, int nCol, const Size& EntrySize, const PointF& EntryOffset, 
					void* Scan0, int Stride, const Size& BmpSize, PixelFormat pf, 
					const Color& FontColor, const Color& BgColor, const ColorPalette* palette, 
					bool Antialiase) const
{
	Bitmap bmpBlock(BmpSize.Width, BmpSize.Height, Stride, pf, reinterpret_cast<BYTE*>(Scan0));
	if (palette)
	{
		bmpBlock.SetPalette(palette);
	}
	Graphics g(&bmpBlock);
	g.SetTextRenderingHint(Antialiase ? TextRenderingHintAntiAlias : TextRenderingHintSingleBitPerPixel);

	g.Clear(BgColor);

	SolidBrush fontBrush(FontColor);

	if (nCol > BmpSize.Height / EntrySize.Height || nRow > BmpSize.Width / EntrySize.Width)
	{
		throw std::invalid_argument("Bitmap too small to hold all the glyphs!");
	}

	for (int i = 0 ; begin < end; ++i, ++begin )
	{
		const wchar_t& c(*begin);
		RectF boundBox;
		g.MeasureString(&c, 1, font, PointF(0.f, 0.f), &boundBox);

		int iCol = i / nRow;					// ceil
		int iRow = i % nRow;

		int entry_x = iRow * EntrySize.Width;
		int entry_y = iCol * EntrySize.Height;

		GraphicsPath gp;
		gp.AddRectangle(Rect(entry_x, entry_y, EntrySize.Width, EntrySize.Height));
		g.SetClip(&gp);

		float x = static_cast<float>(entry_x) + EntryOffset.X;
		float y = static_cast<float>(entry_y) + EntryOffset.Y;

		g.DrawString(&c, 1, font, PointF(x, y), &fontBrush);
	}
}

//----------------------------------------------------------------------------//

StrokeBmpGen::StrokeBmpGen( const wstring& FontName, float FontSize, DotFont::Style Style, 
							DWORD StrokeColor ) 
						   : gg(NULL), strokeColor(StrokeColor)
{
	gg = GlyphGenFactory::create(FontName, FontSize, Style);
	assert(gg);
}

void StrokeBmpGen::gen( wstring::const_iterator begin, const wstring::const_iterator& end,  
					   int nRow, int nCol, const Size& EntrySize, const PointF& EntryOffset, 
					   void* Scan0, int Stride, const Size& BmpSize, PixelFormat pf, 
					   const Color& FontColor, const Color& BgColor, const ColorPalette* palette, 
					   bool Antialiase ) const
{
	gg->gen(begin, end, nRow, nCol, EntrySize, EntryOffset,
		Scan0, Stride, BmpSize, pf, 
		FontColor, BgColor, palette, Antialiase);

	// making stroke
	if (pf.size() / 8 == sizeof(DWORD))
	{
		int cdwStride = Stride / 4;				// stride count in dword
		for (int i = 0; i < nRow; ++i)
		{
			for (int j = 0; j < nCol; ++j)
			{
				DWORD* glyphBeginRow = (DWORD*)((BYTE*)Scan0 + Stride * j * EntrySize.Height + i * EntrySize.Width * pf.size() / 8);
				DWORD* glyphLastRow = (DWORD*)(glyphBeginRow + cdwStride * (EntrySize.Height - 1));

				// stroke the first row
				for (int l = 1; l < EntrySize.Width - 1; ++l)
				{
					if (*(glyphBeginRow + l) != BgColor.GetValue() && *(glyphBeginRow + l) != strokeColor)
					{
						for (DWORD* p = glyphBeginRow + cdwStride + l - 1;
							p <= glyphBeginRow + cdwStride + l + 1; ++p)
						{
							if (*p == BgColor.GetValue())
								*p = strokeColor;
						}
						if (*(glyphBeginRow + l - 1) == BgColor.GetValue())
						{
							*(glyphBeginRow + l - 1) = strokeColor;
						}
						if (*(glyphBeginRow + l + 1) == BgColor.GetValue())
						{
							*(glyphBeginRow + l + 1) = strokeColor;
						}
					}
				}

				// stroke the last row
				for (int l = 1; l < EntrySize.Width - 1; ++l)
				{
					if (*(glyphLastRow + l) != BgColor.GetValue() && *(glyphLastRow + l) != strokeColor)
					{
						for (DWORD* p = glyphLastRow - cdwStride + l - 1;
							p <= glyphLastRow - cdwStride + l + 1; ++p)
						{
							if (*p == BgColor.GetValue())
								*p = strokeColor;
						}
						if (*(glyphLastRow + l - 1) == BgColor.GetValue())
						{
							*(glyphLastRow + l - 1) = strokeColor;
						}
						if (*(glyphLastRow + l + 1) == BgColor.GetValue())
						{
							*(glyphLastRow + l + 1) = strokeColor;
						}
					}
				}

				DWORD* prevPixelRow = glyphBeginRow;
				DWORD* curPixelRow = glyphBeginRow + cdwStride;
				DWORD* nextPixelRow = glyphBeginRow + cdwStride * 2;

				for (int k = 1; k < EntrySize.Height - 1; ++k)
				{
					for (int l = 1; l < EntrySize.Width - 1; ++l)
					{
						if (*(curPixelRow + l) != BgColor.GetValue() && *(curPixelRow + l) != strokeColor)
						{
							for (DWORD* p = prevPixelRow + l - 1;
								p <= prevPixelRow + l + 1; ++p)
							{
								if (*p == BgColor.GetValue())
									*p = strokeColor;
							}
							for (DWORD* p = nextPixelRow + l - 1;
								p <= nextPixelRow + l + 1; ++p)
							{
								if (*p == BgColor.GetValue())
									*p = strokeColor;
							}
							if (*(curPixelRow + l - 1) == BgColor.GetValue())
							{
								*(curPixelRow + l - 1) = strokeColor;
							}
							if (*(curPixelRow + l + 1) == BgColor.GetValue())
							{
								*(curPixelRow + l + 1) = strokeColor;
							}
						}
					}

					prevPixelRow = curPixelRow;
					curPixelRow = nextPixelRow;
					nextPixelRow = glyphBeginRow + cdwStride * (k + 2);
				}
			}
		}
	}
}

StrokeBmpGen::~StrokeBmpGen()
{
	delete gg;
}

//----------------------------------------------------------------------------//




} // namespace dotfont
